/* 
This file is part of JHAVE -- Java Hosted Algorithm Visualization
Environment, developed by Tom Naps, David Furcy (both of the
University of Wisconsin - Oshkosh), Myles McNally (Alma College), and
numerous other contributors who are listed at the http://jhave.org
site

JHAVE is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your
option) any later version.

JHAVE is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
for more details.

You should have received a copy of the GNU General Public License
along with the JHAVE. If not, see:
<http://www.gnu.org/licenses/>.
*/

package exe;

import exe.memorymanager.PointerExpression;
import exe.memorymanager.MemoryManagerException;
import exe.memorymanager.DataExpression;
import exe.memorymanager.BooleanExpression;
import exe.memorymanager.CompoundBooleanExpression;

import java.util.*;
import java.net.URI;
import java.io.*;

/**
 * <p><code>GAIGSMemoryManager</code> provides the ability to implement a 
 * linked list and alsocreate GAIGS visualizations of its state. Use the 
 * various constructorsto specify the general parameters for the list 
 * visualization, and use the <code>toXML</code> method to actually generate 
 * the array XML for snapshots.</p>
 *
 * <p>Many, but not all, of the methods specified by the <code>List</code> 
 * interface areprovided by this class.</p>
 *
 * <p>Methods are also provided to set and get the presentation color of a 
 * list cell.A default list cell color can be set by using the appropriate 
 * constructor. </p>
 *
 *
 * @author David Furcy
 * @version 6/27/07
 */

public class GAIGSMemoryManager extends GAIGSbase 
{
    // name of the three reference boxes in each group
    private static final int LEFT = 0;
    private static final int MIDDLE = 1;
    private static final int RIGHT = 2;
    private static final int ANY = 3;

    // name of the 'data + next-ref-box' in each group
    private static final int NODE = 3;

    // default colors
    private static final String LINE_COLOR = "#000000";  // black
    private static final String FILL_COLOR = "#FFFFFF";  // white
    private static final String HIGHLIGHT_COLOR = "#FF0000";  // red
    private static final String MEMLEAK_FILL_COLOR = "#F5F5F5";  // gray
    private static final String MEMLEAK_LINE_COLOR = "#BBBBBB";  // dark gray
    private static final String DANGLING_LINE_COLOR = "#0000FF";  // blue

    // NULL (pointer value)
    public static Index NULL;
    
    private String source;
    private String formattedSource;
    private String debug;
    private String newline;
    private ArrayList<String> _lines;
    
    //---------------------- Instance Variables ----------------------

    Group grid[][];
    int numRows, numCols;

    double horizSpacing;
    double x1, x2, y1, y2;  // bounds

    int currentRow, currentCol;
    int hDirection, vDirection;

    private Map< String, Index> map;
    ShowFile show;
    String pseudoURL;

    int lineNumber;

    boolean debugMode;

    public void setLineNumber( int n )
    {
	lineNumber = n;
    }//setLineNumber method

    //---------------------- Constructors ----------------------------

    /**
     * Class constructor 
     */

    public GAIGSMemoryManager( ShowFile show, String pseudofile) 
    {
	this(3,4,0,0,0.5, show, pseudofile, "");
    }// default constructor

    /**
     * Class constructor specifying values for all the parameters of the
     * memory manager.
     *
     * @param       name            Display name of this structure.
     * @param       color           Color for items unless locally overridden.
     * @param       x1              Left display bound.
     * @param       y1              Bottom display bound.
     * @param       x2              Top display bound.
     * @param       y2              Right display bound.
     * @param       fontSize        Font size for display.
     */
    public GAIGSMemoryManager( int numRows, int numCols, 
			       int startRow, int startCol,
			       double horizSpacing, 
			       ShowFile show,
			       String pseudofile,
			       String source
 			    ) 
    {

	if ( (numRows <= 0) || (numCols <= 0) )
	    throw new MemoryManagerException( "Unacceptable grid size: " +
					      numRows + " by " + numCols );


	NULL = new Index(-1,-1,-1);
	grid = new Group[numRows][numCols];
	this.numRows = numRows;
	this.numCols = numCols;
	this.currentRow = startRow;
	this.currentCol = startCol;
	this.hDirection = 1;
	this.vDirection = 1;
	this.horizSpacing = horizSpacing;
	this.debugMode = false;
	computeBounds();
	
	this.newline = System.getProperty("line.seperator");
	this.debug = "";
	
	this.source = source;
	// Replace all comment lines with an end line character
	this.source = format_source(source);
	
	//this.formattedSource = format_source(formattedSource);

	_lines = getLines(this.source);
	
	map = new HashMap< String, Index>();
	this.show = show;
	this.pseudoURL = "mm.php?file=" + 
	    pseudofile.replaceAll( "\\\\", "/");	
	    
    }// default constructor


    /**
     * Takes all of the lines and puts them into an ArrayList to be able to format
     * each line individually later 
     */
    private ArrayList<String> getLines(String _s){
    
	ArrayList<String> _lines = new ArrayList<String>();
	
	debug += " ** Debug - Starting getLines() ** " + "\n";
	
	int line_number = -2;
	
	String indentation_const = "  ";
	String indentation = "";
	
	for(int i = 0; i < _s.length(); i++){
	
		String line = "";
		
		while(_s.charAt(i) != '\n' && i < (_s.length() - 1)){
			line += _s.charAt(i);
			i++;
		}   
		
		if(line.contains("}") || i == (_s.length() - 1)){
		  indentation = indentation.replaceFirst(indentation_const, "");
		  debug += "Take away indent: " + line + "\n";
		}
		
		if(line.contains("if"))
		{
		  String temp = "";
		  for(int r = 0; r < line.length(); r++)
		  {
		    if(line.charAt(r) == '(')
		    {
			temp += "(" + " ";
		    }
		    else if(line.charAt(r) == ')')
		    {
		      temp += " " + ")";
		    }
		    else
		    {
		      temp += line.charAt(r);
		    }
		  }
		  line = temp;
		}
		
		else if(line.contains("while"))
		{
		  String temp = "";
		  boolean first = true;
		  for(int r = 0; r < line.length(); r++)
		  {
		    if(line.charAt(r) == '(')
		    {
		      if(first){
			temp += " " + "(" + " ";
			first = false;
		      }
		      else
			temp += "(" + " ";
		    }
		    else if(line.charAt(r) == ')')
		    {
		      temp += " " + ")";
		    }
		    else
		    {
		      temp += line.charAt(r);
		    }
		  }
		  line = temp;
		}
		
		boolean stillAdd = false;
		
		if(i == (_s.length() - 1))
			line += _s.charAt(i);
		
		if(line.equals("{")){
			debug += "LEFT BRACE" + '\n';
			String _newline = _lines.remove(_lines.size()-1);
			if(!_newline.contains("for"))
			{
				_newline += " {";
				_lines.add(_newline);
			}
			else
			{
				_lines.add(_newline);
				line_number++;
				if(line_number < 10)
				{
					_lines.add(" " + line_number + "  " + indentation + "{");
				}
				else
				{
					_lines.add(line_number + "  " + indentation + "{");
				}
			}
		}
		else if(line.contains("for"))
		{
			String temp = "";
			for(int r = 0; r < line.length(); r++)
			{
				if(line.charAt(r) == '(' || (line.charAt(r) == ';' && line.charAt(r-1) != ' ') || line.charAt(r) == '!')
				{
					temp += " " + line.charAt(r);
				}
				else 
					temp += line.charAt(r);
			}
			line = temp;
			
			if(line.contains("{")){
				int index = line.indexOf("{");
				
				line_number++;
				
				if(line_number < 10)
				{
					String temp2 = " " + line_number + "  " + indentation + line.substring(0,index-1);
					_lines.add(temp2);
					line_number++;
					if(line_number < 10)
					{
						_lines.add(" " + line_number + "  " + indentation + "{");
					}
					else
					{
						_lines.add(line_number + "  " + indentation + "{");
					}
				}
				else
				{
					String temp2 = line_number + "  " + indentation + line.substring(0,index-1);
					_lines.add(temp2);
					line_number++;
					if(line_number < 10)
					{
						_lines.add(" " + line_number + "  " + indentation + "{");
					}
					else
					{
						_lines.add(line_number + "  " + indentation + "{");
					}
				}
			}
			else
			{
				line_number++;
				
				if(line_number < 10)
				{
					String temp2 = " " + line_number + "  " + indentation + line;
					_lines.add(temp2);
				}
				else
				{
					String temp2 = line_number + "  " + indentation + line;
					_lines.add(temp2);
				}
			}			
		}
		else if(line.contains("else")){
			debug += "else statement";
			String _newline = _lines.remove(_lines.size()-1);
			if(_newline.contains("{")){
				debug += " contains {" + _newline + '\n';
				_newline += " else {";
				_lines.add(_newline);
			}
			else if(_newline.contains("}")){
				_newline += " else ";
				_lines.add(_newline);
			}
			else{
				debug += " does not contain {" + _newline + '\n';
				_lines.add(_newline);
				stillAdd = true;
			}
		}
		else if(line.contains("if") && line.contains("break"))
		{
			line_number++;
			int index = line.indexOf("break");
			if(line_number < 10)
			{
				indentation += indentation_const;
				String temp = " " + line_number + "  "  + indentation + line.substring(0,index-1);
				_lines.add(temp);
				line_number++;
				if(line_number < 10)
				{
					int new_index = line.indexOf("}");
					indentation += indentation_const;
					temp = " " + line_number + "  "  + indentation + line.substring(index-1, new_index);
					_lines.add(temp);
					line_number++;
					if(line_number < 10)
					{
						_lines.add(" " + line_number + "  "  + indentation + "}");					
					}
					else
					{
						_lines.add(line_number + "  "  + indentation + "}");
					}
					indentation = indentation.replaceFirst(indentation_const, "");
				}
				else
				{
					int new_index = line.indexOf("}");
					indentation += indentation_const;
					temp = line_number + "  "  + indentation + line.substring(index-1, new_index);
					_lines.add(temp);
					line_number++;
					if(line_number < 10)
					{
						_lines.add(" " + line_number + "  "  + indentation + "}");					
					}
					else
					{
						_lines.add(line_number + "  "  + indentation + "}");
					}
					indentation = indentation.replaceFirst(indentation_const, "");
				}
			}
			else
			{
				String temp = line_number + "  "  + indentation + line.substring(0,index-1);
				_lines.add(temp);
				line_number++;
				if(line_number < 10)
				{
					int new_index = line.indexOf("}");
					temp = " " + line_number + "  "  + indentation + indentation_const + line.substring(index-1, new_index);
					_lines.add(temp);
					line_number++;
					if(line_number < 10)
					{
						_lines.add(" " + line_number + "  "  + indentation + "}");					
					}
					else
					{
						_lines.add(line_number + "  "  + indentation + "}");
					}
				}
				else
				{
					int new_index = line.indexOf("}");
					temp = line_number + "  " +  indentation + indentation_const + line.substring(index-1, new_index);
					_lines.add(temp);
					line_number++;
					if(line_number < 10)
					{
						_lines.add(" " + line_number + "  " + indentation + "}");					
					}
					else
					{
						_lines.add(line_number + "  " + indentation + "}");
					}
				}
			}
		}
		else if(line.contains("&&"))
		{
		  
		  
		  
		  String line1_number;
		  String line2_number;
		  line_number++;		 
		  if(line_number < 10){
		    line1_number = " " + line_number + "  ";
		  }
		  else{
		    line1_number = line_number + "  ";
		  }
		  line_number++;
		  if(line_number < 10){
		    line2_number = " " + line_number + "  ";
		  }
		  else{
		    line2_number = line_number + "  ";
		  }
		  
		  String line1 = line1_number + line.substring(0,line.indexOf("&&")+2);
		  
		  String padding = "";
		  
		  // index of while + 5
		  int index = line1.indexOf("while") + 5;
		  // then look for the next character and start there
		  int answer = 0;
		  for(int c = index; c < line1.length(); c++)
		  {
		    if(line1.charAt(c) >= 'A' && line1.charAt(c) <= 'z')
		    {
		      debug += "CHARAT (" + c + ") is: " + line1.charAt(c);
		      answer = c;
		      break;
		    }
		  }
		  
		  for(int d = 0; d < answer - line2_number.length() - (line1_number.length()-1); d++)
		  {
		    padding += " ";
		  }
		  
		  String line2 = line2_number + padding + line.substring(line.indexOf("&&")+2);
		  
		  _lines.add(line1);
		  _lines.add(line2);
		}
		else {
			line_number++;		

			if(line_number >= 1 && line_number < 10){
			        line = " " + line_number + "  " + indentation + line;
				_lines.add(line);
			}
			else if(line_number >= 10){
				line = line_number + "  " + indentation + line;
				_lines.add(line);
			}
			else
				_lines.add("<b>" + line + "</b>");		
			
			if(line_number >= 1 && line_number < 10){
				if(!line.equals("\n"))
					debug += " " + line_number + "  " + indentation + line + '\n';
				else
					debug += line;
			}
			else if(line_number >= 10){
				if(!line.equals("\n"))
					debug += line_number + "  " + indentation + line + '\n';
				else
					debug += line;
			}
			else
				debug += line + '\n';
		}
		
		if(stillAdd) {
			line_number++;
		
			if(line_number >= 1 && line_number < 10)
				_lines.add(" " + line_number + "  " + indentation + line);
			else if(line_number >= 10)
				_lines.add(line_number + "  " + indentation + line);
			else{
				_lines.add("<b>" + line + "</b>");
			}		
			
			if(line_number >= 1 && line_number < 10){
				if(!line.equals("\n"))
					debug += " " + line_number + "  " + line + '\n';
				else
					debug += line;
			}
			else if(line_number >= 10){
				if(!line.equals("\n"))
					debug += line_number + "  " + line + '\n';
				else
					debug += line;
			}
			else
				debug += line + '\n';
		}
		
		if(line.contains("{")){
		  indentation += indentation_const;
		  debug += "Indent" + "\n";
		}
		
	}
	
	return _lines;
	    
    }    
    
    /**
     * Takes out all comment lines
     */
    private String format_source(String _source){
    
      String temp = "";
      
      // Remove comments
      temp = _source.replaceAll("[//][//][\\w|\\s]*[\\n]","\n");
      
      return temp;
      
    }

    /**
     * Displays the code with the specified line number highlighted.
     */
    private String make_uri(int line_number, int start, int end, int color){
    
	    
	    String content = "";
	    
      if(debugMode){
	content = "<html><head><title>Program</title></head> <body> <pre> " + highlight_source(line_number, start, end, source, color) + " </pre> <pre> " + debug + " </pre> </body> </html>";   
      }
      else {
          content = "<html><head><title>Program</title></head> <body> <pre> " + highlight_source(line_number, start, end, source, color) + " </pre> </body> </html>";
      }
      
      URI uri = null;
      
      try {
	
	uri = new URI("str",content,"");
	
      }
      catch(java.net.URISyntaxException e){
      }
      
      return uri.toASCIIString();
      
    }
    
    /**
     * Highlights the specified line of code.
     */
    private String highlight_source(int line_number, int start, int end, String _source, int color){
    
      String _pseudocode = "";
      
      for(int i = 0; i < _lines.size(); i++){
      
	String temp = _lines.get(i);
	
	  if(_lines.get(i).contains("//"))
	      _pseudocode += '\n';	
          else if(line_number < 10){
	    if(_lines.get(i).contains(" " + line_number)){
	      // do something
	      if(start > -1)
	      {
		if(end > -1 && end < temp.length() && start != end)
		{ 
		   if(i != 1){
		      _pseudocode += temp.substring(0,start) + "<font color='red'>" + temp.substring(start,end+1) + "</font>" + temp.substring(end+1,temp.length()) + '\n';
		   }
		}
		else
		{
		   if(i != 1){
		       _pseudocode += temp.substring(0,start) + "<font color='red'>" + temp.substring(start,temp.length()) + "</font>" + '\n';
		   }
		}
	      }
	      else{
		 _pseudocode += "<font color='red'>" + _lines.get(i) + "</font>" + '\n';
	      }
	    }
	    else{
	      _pseudocode += _lines.get(i) + '\n';
	    }
	  }
	  else if(line_number >= 10){
	    if(_lines.get(i).contains(line_number + "")){
	      // do something
	       if(start > -1)
	       {
		if(end > -1 && end < temp.length() && start != end)
		{ 
		  _pseudocode += temp.substring(0,start) + "<font color='red'>" + temp.substring(start,end+1) + "</font>" + temp.substring(end+1,temp.length()) + '\n';
		}
		else
		{
		  _pseudocode += temp.substring(0,start) + "<font color='red'>" + temp.substring(start,temp.length()) + "</font>" + '\n';
		}
	      }
	      else
		_pseudocode += "<font color='red'>" + _lines.get(i) + "</font>" + '\n';
	    }
	    else{
	      _pseudocode += _lines.get(i) + '\n';
	    }
	  }
	  else{
	    _pseudocode += _lines.get(i) + '\n';
	  }
	      
      }
      
      return _pseudocode;
      
    }

    public void setDebugMode( boolean flag )
    {
	debugMode = flag;
    }//setDebugMode method

    private void computeBounds()
    {
	double height = 5*numRows; // each row is 5-boxWidth tall
	double width = (3+4*horizSpacing)*numCols; // each cell contains
  	                              // 3 boxes plus 4 inter-box spaces

	// the bounds define a square of size 1.1
	// centered in the area between (-0.05, -0.05) and (1.05, 1.05)

	if ( width >= height )
	{
	    double adjustedHeight = height / width;

	    x1 = -0.05;     y1 =  (0.95 - adjustedHeight) / 2;
	    x2 =  x1 + 1.1; y2 =  y1 + 1.1;

	}
	else
	{
	    //height is larger than width
	    double adjustedWidth =  width / height;

	    x1 = -0.05 + (1.1 - adjustedWidth)/2;      y1 = -0.05;
	    x2 =  x1 + 1.1;                            y2 = y1 + 1.1;
	}

    }//computeBounds method


    //---------- methods for pointer operations --------------------------

    /**
     * Implements the following intruction: Node *p = new( info, next).
     * By default, the label is positioned above the box.
     *
     * @param    p     Name of the pointer variable being declared.
     * @param    info  Data to be stored in the new node being allocated.    
     * @param    next  Pointer expression whose value is to be stored in
     *                 the next field of the node.
     */
    public void declareAllocateAndAssign( String p, String info, 
					  PointerExpression next,
					  int row, int col, String ref)
	throws IOException
    {
	//System.out.println("desired: " + row + " " + col + " " + ref);
	declareAllocateAndAssign( p, info, next,  "above", row, col, ref );
    }//declareAllocateAndAssign method


    // used for break statement
    public void doNothing( int lineNumber )
	throws IOException
    {
	System.out.println("Break");
	//show.writeSnap("", "JHAVEPOP.htm", pseudoURL + "&line=" + lineNumber, this);
	show.writeSnap("", "JHAVEPOP.htm", make_uri(lineNumber,0,0,0) ,this);
    }//doNothing method

    /**
     * Implements the following intruction: Node *p = new( info, next).
     * The label is positioned according to the fourth argument.
     *
     * @param    p        Name of the pointer variable being declared.
     * @param    info     Data to be stored in the new node being allocated.
     * @param    next     Pointer expression whose value is to be stored in
     *                    the next field of the node.
     * @param    position Position of the reference label.
     */
    public void declareAllocateAndAssign( String p, String info, 
					  PointerExpression next,
					  String position,
					  int drow, int dcol, String dref)
	throws IOException
    {
	if (map.get( p ) != null)
	    throw new MemoryManagerException( p + " is being redefined" );

	Index index = allocateRefAndNode(  makeIndex( drow, dcol, dref) );

	//System.out.print("allocated: ");
	//System.out.println( index );

	int row = index.getRow();
	int col = index.getCol();
	int ref = index.getRef();
	Group g;
	if (grid[ row ][ col ] != null)
	    g = grid[ row ][ col ];
	else g = new Group();

	g.setRow( row );
	g.setCol( col );

	switch (ref) {
	case LEFT:
	    g.setLeftRef( new Box( HIGHLIGHT_COLOR ) );
	    g.setLeftLabel( p );
	    g.setLeftLabelPosition( position );
	    g.setLeftArrow( new Arrow( row, col, "regular", HIGHLIGHT_COLOR ));
	    break;
	case MIDDLE:
	    g.setMiddleRef( new Box( HIGHLIGHT_COLOR ) );
	    g.setMiddleLabel( p );
	    g.setMiddleLabelPosition( position );
	    g.setMiddleArrow( new Arrow( row, col, "regular", HIGHLIGHT_COLOR ) );
	    break;
	case RIGHT:
	    g.setRightRef( new Box( HIGHLIGHT_COLOR ) );
	    g.setRightLabel( p );
	    g.setRightLabelPosition( position );
	    g.setRightArrow( new Arrow( row, col, "regular", HIGHLIGHT_COLOR ) );
	    break;
	}//switch

	g.setDataBox( new Box( HIGHLIGHT_COLOR ) );
	g.setDataValue( info );
	g.setNextRef( new Box( HIGHLIGHT_COLOR ) );

	if (next.isNull() )
	    g.setNextArrow( new Arrow( -1, -1, "null", HIGHLIGHT_COLOR) );
	else
	{
	    String id = next.getPointerName();
	    int length = next.getChainLength();
	    //System.out.println( id + " " + length);
	    Index nextIndex;
	    if (length==0)
		nextIndex = getAddress(id);
	    else
		nextIndex = getValue( getNext( getAddress(id), length ) );
	    g.setNextArrow( new Arrow( nextIndex.getRow(),
				       nextIndex.getCol(), 
				       "regular", HIGHLIGHT_COLOR) );
	}

	grid[ row ][ col ] = g;
	
	map.put( p, index );

	//show.writeSnap("", "JHAVEPOP.htm", pseudoURL + "&line=" + lineNumber, this);
	show.writeSnap("", "JHAVEPOP.htm", make_uri(lineNumber,-1,-1,-1) ,this);

	switch (ref) {
	case LEFT:
	    g.getLeftRef().setLineColor( LINE_COLOR );
	    g.getLeftArrow().setLineColor( LINE_COLOR );
	    break;
	case MIDDLE:
	    g.getMiddleRef().setLineColor( LINE_COLOR );
	    g.getMiddleArrow().setLineColor( LINE_COLOR );
	    break;
	case RIGHT:
	    g.getRightRef().setLineColor( LINE_COLOR );
	    g.getRightArrow().setLineColor( LINE_COLOR );
	    break;
	}//switch

	g.getNextRef().setLineColor( LINE_COLOR );
	g.getDataBox().setLineColor( LINE_COLOR );
	g.getNextArrow().setLineColor( LINE_COLOR );

    }//declareAllocateAndAssign method



    public void declareAndAssign( String p, 
				  PointerExpression RHS,
				  int drow, int dcol, String dref,
				  String position)
	throws IOException
    {

	//System.out.println( "in declareAndAssign: " + drow + dcol + dref);
	if (map.get( p ) != null)
	    throw new MemoryManagerException( p + " is being redefined" );


	Index index = allocateRef(  makeIndex( drow, dcol, dref) );


	int row = index.getRow();
	int col = index.getCol();
	int ref = index.getRef();

	//System.out.println( "in declareAndAssign2: " + row + col + ref);

	Group g;
	if (grid[ row ][ col ] != null)
	    g = grid[ row ][ col ];
	else g = new Group();

	g.setRow( row );
	g.setCol( col );

	Arrow arrow;
	if (RHS.isNull() )
	    arrow = new Arrow( -1, -1, "null", HIGHLIGHT_COLOR);
	else
	{
	    String id = RHS.getPointerName();
	    int length = RHS.getChainLength();
	    Index nextIndex;
	    if (length==0)
		nextIndex = getValue(id);
	    else
		nextIndex = getValue( getNext( getAddress(id), length ) );
	    //System.out.println( nextIndex );
	    arrow = new Arrow( nextIndex.getRow(),
			       nextIndex.getCol(), 
			       "regular", HIGHLIGHT_COLOR);
	}

	switch (ref) {
	case LEFT:
	    g.setLeftRef( new Box( HIGHLIGHT_COLOR ) );
	    g.setLeftLabel( p );
	    if (!position.equals(""))
		g.setLeftLabelPosition( position );
	    else
		g.setLeftLabelPosition( "above" );
	    g.setLeftArrow( arrow );
	    break;
	case MIDDLE:
	    g.setMiddleRef( new Box( HIGHLIGHT_COLOR ) );
	    g.setMiddleLabel( p );
	    if (!position.equals(""))
		g.setMiddleLabelPosition( position );
	    else
		g.setMiddleLabelPosition( "above" );
	    g.setMiddleArrow( arrow );
	    break;
	case RIGHT:
	    g.setRightRef( new Box( HIGHLIGHT_COLOR ) );
	    g.setRightLabel( p );
	    if (!position.equals(""))
		g.setRightLabelPosition( position );
	    else
		g.setRightLabelPosition( "above" );
	    g.setRightArrow( arrow );
	    break;
	}//switch


	grid[ row ][ col ] = g;
	
	map.put( p, index );

	//show.writeSnap("", "JHAVEPOP.htm", pseudoURL + "&line=" + lineNumber, this);
	show.writeSnap("", "JHAVEPOP.htm", make_uri(lineNumber,-1,-1,-1) ,this);

	switch (ref) {
	case LEFT:
	    g.getLeftRef().setLineColor( LINE_COLOR );
	    g.getLeftArrow().setLineColor( LINE_COLOR );
	    break;
	case MIDDLE:
	    g.getMiddleRef().setLineColor( LINE_COLOR );
	    g.getMiddleArrow().setLineColor( LINE_COLOR );
	    break;
	case RIGHT:
	    g.getRightRef().setLineColor( LINE_COLOR );
	    g.getRightArrow().setLineColor( LINE_COLOR );
	    break;
	}//switch

    }//declareAndAssign method







    /**
     * Implements the following intruction:   Node *p; together with a full
     * specification of the pointer variable's address.
     * The label is positioned by default.
     *
     * @param    p        Name of the pointer variable being declared.
     * @param    row      Row of the requested address.
     * @param    col      Column of the requested address.     
     * @param    ref      Reference location of the requested address.
     */
    public void declare( String p, int row, int col, String ref)
	throws IOException
    {
	declare( p, "above", row, col, ref);
    }//declare method

    /**
     * Implements the following intruction:   Node *p; together with a full
     * specification of requested the pointer variable's address
     *
     * @param    p        Name of the pointer variable being declared.
     * @param    position Position of the reference label.
     * @param    row      Row of the requested address.
     * @param    col      Column of the requested address.     
     * @param    ref      Reference location of the requested address.
     */
    public void declare( String p, String position, 
			 int drow, int dcol, String dref )
	throws IOException
    {
	if (map.get( p ) != null)
	    throw new MemoryManagerException( p + " is being redefined" );

	Index index = allocateRef( makeIndex( drow, dcol, dref ) );
	int row = index.getRow();
	int col = index.getCol();
	int ref = index.getRef();

	Group g;
	if ( grid[row][col]!=null)
	    g = grid[row][col];
	else
	    g  = new Group();

	g.setRow( row );
	g.setCol( col );

	switch (ref) {
	case LEFT:
	    g.setLeftRef( new Box( HIGHLIGHT_COLOR ) );
	    g.setLeftLabel( p );
	    g.setLeftLabelPosition( position );
	    break;
	case MIDDLE:
	    g.setMiddleRef( new Box( HIGHLIGHT_COLOR ) );
	    g.setMiddleLabel( p );
	    g.setMiddleLabelPosition( position );
	    break;
	case RIGHT:
	    g.setRightRef( new Box( HIGHLIGHT_COLOR ) );
	    g.setRightLabel( p );
	    g.setRightLabelPosition( position );
	    break;
	}//switch

	if (grid[ row ][ col ] == null)
	    grid[ row ][ col ] = g;
	
	map.put( p, new Index(row,col,ref) );

	//show.writeSnap("", "JHAVEPOP.htm", pseudoURL + "&line=" + lineNumber, this);
	show.writeSnap("", "JHAVEPOP.htm", make_uri(lineNumber,-1,-1,-1) ,this);

	switch (ref) {
	case LEFT:
	    g.getLeftRef().setLineColor(LINE_COLOR );
	    break;
	case MIDDLE:
	    g.getMiddleRef().setLineColor(LINE_COLOR );
	    break;
	case RIGHT:
	    g.getRightRef().setLineColor(LINE_COLOR );
	    break;
	}//switch
       
    }//declare method


    /**
     * Implements the following intruction: p = new( info, next), where
     * pointer p was previously declared.
     *
     * @param    p        Name of the pointer variable being assigned.
     * @param    info     Data to be stored in the new node being allocated.
     * @param    next     Pointer expression whose value is to be stored in
     *                    the next field of the node.
     */

    public void allocateAndSet( String p, String info, String next )
	throws IOException
    {
	Index refIndex = map.get( p );
	if ( p == null )
	  throw new MemoryManagerException( "Undeclared pointer variable: "+p);

	Index index = allocateNode( refIndex.getRow(), refIndex.getCol() );

	Group g;

	if ( (index.getRow() != refIndex.getRow()) ||
	     (index.getCol() != refIndex.getCol()) )
	{
	    // the cell containing p was not available
	    int row = index.getRow();
	    int col = index.getCol();
	    int ref = NODE;
	    g  = new Group();
	    
	    g.setRow( row );
	    g.setCol( col );
	    g.setLeftArrow( new Arrow( row, col, "regular", HIGHLIGHT_COLOR ));
	    g.setDataBox( new Box( HIGHLIGHT_COLOR ) );
	    g.setDataValue( info );
	    g.setNextRef( new Box( HIGHLIGHT_COLOR ) );
	    if (next.equals( "null" ) )
		g.setNextArrow( new Arrow( -1, -1, "null", HIGHLIGHT_COLOR) );
	    else
		{
		    Index nextIndex = getValue( next );
		    g.setNextArrow( new Arrow( nextIndex.getRow(),
					       nextIndex.getCol(), 
				       "regular", HIGHLIGHT_COLOR) );
		}

	    grid[ row ][ col ] = g;
	}
	else
	{   // the cell containing p has room for the node
	    g = grid[ refIndex.getRow() ][ refIndex.getCol() ];
	    g.setLeftArrow( new Arrow( refIndex.getRow(), 
				       refIndex.getCol(), 
				       "regular", HIGHLIGHT_COLOR ));
	    g.setDataBox( new Box( HIGHLIGHT_COLOR ) );
	    g.setDataValue( info );
	    g.setNextRef( new Box( HIGHLIGHT_COLOR ) );
	    if (next.equals( "null" ) )
		g.setNextArrow( new Arrow( -1, -1, "null", HIGHLIGHT_COLOR) );
	    else
		{
		    Index nextIndex = getValue( next );
		    g.setNextArrow( new Arrow( nextIndex.getRow(),
					       nextIndex.getCol(), 
				       "regular", HIGHLIGHT_COLOR) );
		}
	}

	//show.writeSnap("", "JHAVEPOP.htm", pseudoURL + "&line=" + lineNumber, this);	
	show.writeSnap("", "JHAVEPOP.htm", make_uri(lineNumber,-1,-1,-1) ,this);

	g.getLeftArrow().setLineColor( LINE_COLOR );
	g.getNextRef().setLineColor( LINE_COLOR );
	g.getDataBox().setLineColor( LINE_COLOR );
	g.getNextArrow().setLineColor( LINE_COLOR );
    }//allocateAndSet method

    /**
     * Evaluate the following expreeion: new( info, next)
     *
     * @param    info     Data to be stored in the new node being allocated.
     * @param    next     Pointer expression whose value is to be stored in
     *                    the next field of the node.
     * @return   the address allocated for the new node
     */

    /*

    public Index allocate( String info, String next )
	throws IOException
    {

	Index index = allocateNode( NULL );
	int row = index.getRow();
	int col = index.getCol();
	int ref = LEFT;
	Group g  = new Group();

	g.setRow( row );
	g.setCol( col );
	g.setDataBox( new Box( HIGHLIGHT_COLOR ) );
	g.setDataValue( info );
	g.setNextRef( new Box( HIGHLIGHT_COLOR ) );
	if (next.equals( "null" ) )
	    g.setNextArrow( new Arrow( -1, -1, "null", HIGHLIGHT_COLOR) );
	else
	{
	    Index nextIndex = getValue( next );
	    g.setNextArrow( new Arrow( nextIndex.getRow(),
				       nextIndex.getCol(), 
				       "regular", HIGHLIGHT_COLOR) );
	}

	grid[ row ][ col ] = g;


    }//allocate method
    */	

    public void delete( Index address ) throws IOException
    {
	Index index = getValue( address );

        if (index.getRef() != NODE)   /* should never happen */
	    throw new MemoryManagerException( "Trying to deallocate a memory "
	     + "location that does not contain a Node"  );



	/* deallocate the memory */
	int row = index.getRow();
	int col = index.getCol();

	// remember the next node (if any) to check for memory leaks
	Arrow next =  grid[ row ][ col ].getNextArrow();

        grid[ row ][ col ].setDataBox( null );
        grid[ row ][ col ].setNextRef( null );
        grid[ row ][ col ].setDataValue( null );
        grid[ row ][ col ].setNextArrow( null );

        if ( (grid[ row ][ col ].getLeftRef() == null) &&
	     (grid[ row ][ col ].getMiddleRef() == null) &&
	     (grid[ row ][ col ].getRightRef() == null)	    
	     )
	    grid[ row ][ col ] = null;

	/* invalidate the pointer */
	int ro = address.getRow();
	int co = address.getCol();
	int ref = address.getRef();

        Box box;

	switch (ref) {
	case LEFT:   
	    box = grid[ ro ][ co ].getLeftRef(); 
	    grid[ ro ][ co ].setLeftArrow( null ); 
	    break;
	case MIDDLE: 
	    box = grid[ ro ][ co ].getMiddleRef(); 
	    grid[ ro ][ co ].setMiddleArrow( null ); 
	    break;
	case RIGHT:  
	    box = grid[ ro ][ co ].getRightRef(); 
	    grid[ ro ][ co ].setRightArrow( null ); 
	    break;
	default:     
	    box = grid[ ro ][ co ].getNextRef(); 
	    grid[ ro ][ co ].setNextArrow( null ); 
	    break;
	}

	if ( (next != null) &&
	     (next.getType().equals("regular")) )

	    checkForMemoryLeak( next.getRow(), next.getCol() );

	checkForDanglingPointers( row, col );

	box.setLineColor( HIGHLIGHT_COLOR );
	//show.writeSnap("", "JHAVEPOP.htm", pseudoURL + "&line=" + lineNumber, this);
	show.writeSnap("", "JHAVEPOP.htm", make_uri(lineNumber,-1,-1,-1) ,this);
	box.setLineColor( LINE_COLOR );

    }//delete method

    /**
     * Assignment operator between pointer variables/expressions
     *
     * @param       address    The address (index) of the left-hand side.
     * @param       value      The value (index) of the right-hand side.
     */
    public void assign( Index address, Index value,
			int start, int end) throws IOException
    {        
	int row = address.getRow();
	int col = address.getCol();
	int id = address.getRef();
	
	Arrow arrow;
	if ( value.equals( NULL ) )
	    arrow = new Arrow( -1,-1, "null", HIGHLIGHT_COLOR );
	else
	    arrow = new Arrow( value.getRow(), value.getCol(), 
				 "regular", HIGHLIGHT_COLOR);

	Arrow oldArrow;

	switch (id) { 
	case LEFT: 
	    oldArrow = grid[ row ][ col ].getLeftArrow();
	    grid[ row ][ col ].setLeftArrow( arrow );
	    break;
	case MIDDLE: 
	    oldArrow = grid[ row ][ col ].getMiddleArrow();
	    grid[ row ][ col ].setMiddleArrow( arrow );
	    break;
	case RIGHT: 
	    oldArrow = grid[ row ][ col ].getRightArrow();
	    grid[ row ][ col ].setRightArrow( arrow );
	    break;
	default: 
	    oldArrow = grid[ row ][ col ].getNextArrow();
	    grid[ row ][ col ].setNextArrow( arrow );
	}// switch on reference box

	if ( (oldArrow != null) && oldArrow.getType().equals("regular"))
	{
	    int pRow = oldArrow.getRow(); // index of node previously pointed
	    int pCol = oldArrow.getCol(); // to by the assigned pointer
	    checkForMemoryLeak( pRow, pCol );
	}

        if (start>-1) //show.writeSnap("", "JHAVEPOP.htm", pseudoURL + "&line=" + lineNumber + "&amp;start=" + start + "&amp;end=" + end, this);
	    show.writeSnap("", "JHAVEPOP.htm", make_uri(lineNumber,start,end,-1) ,this);
        else //show.writeSnap("", "JHAVEPOP.htm", pseudoURL + "&line=" + lineNumber, this);
	    show.writeSnap("", "JHAVEPOP.htm", make_uri(lineNumber,-1,-1,-1) ,this);
	arrow.setLineColor( LINE_COLOR );

	//* needed when used, for example, in: p->next = new Node( ... )
	if ( !value.equals( NULL ) )
	{
	    grid[ value.getRow() ][ value.getCol() ].getDataBox().setLineColor( LINE_COLOR );
	    grid[ value.getRow() ][ value.getCol() ].getNextRef().setLineColor( LINE_COLOR );
	    grid[ value.getRow() ][ value.getCol() ].getNextArrow().setLineColor( LINE_COLOR );
	}
    }// assign method


    private void checkForMemoryLeak( int row, int col )
    {
	Arrow arrow;

	if  ( (grid[ row ][ col ] != null) &&
	      (grid[ row ][ col ].getDataBox() != null) )
	
        {   // there is a node at location, that is, the old pointer was
	    // NOT a dangling pointer

	for(int c=0; c<numCols; c++)
	    for(int r=0; r<numRows; r++)
		if ( grid[ r ][ c ] != null)
		{
		    if ( ( (arrow = grid[ r ][ c ].getLeftArrow()) != null ) &&
			 ( arrow.getRow() == row ) &&
			 ( arrow.getCol() == col ) )
			return; // the node at row x col is NOT an orphan

		    if ( ( (arrow = grid[ r ][ c ].getMiddleArrow()) != null ) &&
			 ( arrow.getRow() == row ) &&
			 ( arrow.getCol() == col ) )
			return; // the node at row x col is NOT an orphan
		    if ( ( (arrow = grid[ r ][ c ].getRightArrow()) != null ) &&
			 ( arrow.getRow() == row ) &&
			 ( arrow.getCol() == col ) )
			return; // the node at row x col is NOT an orphan

		    if ( ( ! grid[ r ][ c ].orphanNode ) &&
			 ( (arrow = grid[ r ][ c ].getNextArrow()) != null ) &&
			 ( arrow.getRow() == row ) &&
			 ( arrow.getCol() == col ) )
			return; // the node at row x col is NOT an orphan
		}

	// a memory leak was found at this node
	grid[ row ][ col ].orphanNode = true;
	grid[ row ][ col ].getDataBox().setLineColor( MEMLEAK_LINE_COLOR );
	grid[ row ][ col ].getDataBox().setFillColor( MEMLEAK_FILL_COLOR );
	grid[ row ][ col ].getNextRef().setLineColor( MEMLEAK_LINE_COLOR );
	grid[ row ][ col ].getNextRef().setFillColor( MEMLEAK_FILL_COLOR );
	grid[ row ][ col ].getNextArrow().setLineColor( MEMLEAK_LINE_COLOR );

	// check the rest of the linked list (if any)
	if ( ( (arrow = grid[ row ][ col ].getNextArrow() ) != null) &&
	     (arrow.getType().equals("regular")) )

	    checkForMemoryLeak( arrow.getRow(), arrow.getCol() );

	}//if there is a node at that location
    }


    private void checkForDanglingPointers( int row, int col )
    {
	Arrow arrow;

	for(int c=0; c<numCols; c++)
	    for(int r=0; r<numRows; r++)
		if ( grid[ r ][ c ] != null)
		{
		    if ( ( (arrow = grid[ r ][ c ].getLeftArrow()) != null ) &&
			 ( arrow.getRow() == row ) &&
			 ( arrow.getCol() == col ) )

			// found a dangling pointer
			arrow.setLineColor( DANGLING_LINE_COLOR );

		    if ( ( (arrow = grid[ r ][ c ].getMiddleArrow()) != null ) &&
			 ( arrow.getRow() == row ) &&
			 ( arrow.getCol() == col ) )

			// found a dangling pointer
			arrow.setLineColor( DANGLING_LINE_COLOR );

		    if ( ( (arrow = grid[ r ][ c ].getRightArrow()) != null ) &&
			 ( arrow.getRow() == row ) &&
			 ( arrow.getCol() == col ) )

			// found a dangling pointer
			arrow.setLineColor( DANGLING_LINE_COLOR );

		    if ( ( ! grid[ r ][ c ].orphanNode ) &&
			 ( (arrow = grid[ r ][ c ].getNextArrow()) != null ) &&
			 ( arrow.getRow() == row ) &&
			 ( arrow.getCol() == col ) )

			// found a dangling pointer
			arrow.setLineColor( DANGLING_LINE_COLOR );
		}

    }



    /**
     * Returns the address of a pointer, if it is declared;
     * throws a MemoryManagerException, otherwise.
     * 
     * @param     name     The name of a pointer. 
     * @return    The address (or index) of the pointer.
     */
    public Index getAddress( String name )
    {
	Index index = map.get( name );

	if ( index == null )
	    throw new MemoryManagerException(
  	         "Undefined pointer: " + name);

	return index;
    }//getAddress method


    public Index getAddress( PointerExpression p )
    {
	//System.out.println("in getAddress: " + p.getPointerName() + " " +
	//p.getChainLength());
	if (p.isNull())
	    return NULL;
	else if (p.getChainLength() == 0)
	    return getAddress( p.getPointerName() );
	else
	    return getNext( getAddress( p.getPointerName() ),
			p.getChainLength() );
    }

    /**
     * Returns the value of a pointer variable, if it is declared
     * and initialised; throws a MemoryManagerException, otherwise.
     * 
     * @param     name     The name of a pointer. 
     * @return    The value (i.e., an address) of the pointer.
     */
    public Index getValue( String name )
    {
	Index index = map.get( name );

	if ( index == null )
	    throw new MemoryManagerException(
  	         "Undefined pointer: " + name);

	return getValue( index );

    }//getValue method

    /**
     * Returns the value of a pointer variable, if it is declared
     * and initialised; throws a MemoryManagerException, otherwise.
     * 
     * @param     address  The address (i.e., index) of a pointer. 
     * @return    The value (i.e., an address) of the pointer.
     */
    public Index getValue( Index address )
    {
	if (address.equals(NULL)) return NULL;

	Arrow arrow = getArrow( address );

	if ( arrow.getType().equals("null"))
	    return NULL;
	else
	    return new Index( arrow.getRow(), arrow.getCol(), NODE );

    }//getValue method
			
    /**
     * Follows a chain of next pointers of a given length.
     *
     * @param      address    Index of the first node in the chain.
     * @param      length     Number (> 0) of next pointers to dereference.
     * @return     The index of the last pointer in the chain.
     */	
    public Index getNext( Index address, int length )
    {
	Arrow arrow = getArrow( address );

	if (arrow.getType().equals("null"))
	    throw new MemoryManagerException( 
	       "Trying to dereference a NULL pointer" );

	Index nextIndex = new Index( arrow.getRow(), 
				     arrow.getCol(), 
				     NODE );
	
	if (length == 1)
	    return nextIndex;
	else
	    return getNext( nextIndex, length - 1);
    }//getNext method

    /**
     * Allocates space for a new pointer (left reference) and a new 
     * node in the grid.
     *
     * @return  The index of the grid cell selected according to the 
     *          default layout.
     */
    private Index allocateRefAndNode( Index desired ) 
	throws MemoryManagerException
    {
	//System.out.println( desired == NULL );
	if (!desired.equals(NULL))
	{    // a particular cell was requested
	    
	    int drow = desired.getRow();
	    int dcol = desired.getCol();
	    Group g = grid[ drow ][ dcol ];

	    if ( g == null )  // desired cell is empty

		if ( (desired.getRef() == ANY) ||
		     (desired.getRef() == LEFT) )
		    return new Index( drow, dcol, LEFT );
		else if (desired.getRef() == MIDDLE)
		    return new Index( drow, dcol, MIDDLE );
		else return new Index( drow, dcol, RIGHT );

	    else // desired cell already holds a group

		if ( g.getDataBox() == null )
		    
		    // cell does not hold a node yet		    
		    if ( ((desired.getRef() == ANY) ||
			  (desired.getRef() == LEFT)) &&
			 (g.getLeftRef() == null) )
			return new Index( drow, dcol, LEFT );
		    else if ( ((desired.getRef() == ANY) ||
			       (desired.getRef() == MIDDLE)) &&
			      (g.getMiddleRef() == null) )
			return new Index( drow, dcol, MIDDLE );
		    else if ( ((desired.getRef() == ANY) ||
			       (desired.getRef() == RIGHT)) &&
			      (g.getRightRef() == null) )
			return new Index( drow, dcol, RIGHT );
		    else // all three references are used
			{ }
	
	}// the user requested a particular cell

	// the user did not specify a cell or the desired cell was not
	// available for both a node and a reference
	
	
	for(int col=0; col<numCols; col++)
	    for(int row=0; row<numRows; row++)
	{
	    if (grid[ row ][ col ] == null)
		return new Index(row, col, LEFT);
	    else
	    {   // current cell already holds a group g
		Group g = grid[ row ][ col ];

		if (g.getDataBox() == null)  // node is available
		{
		    if (g.getLeftRef() == null)
			return new Index( row, col, LEFT );
		    else if (g.getRightRef() == null)
			return new Index( row, col, RIGHT );
		    else if (g.getMiddleRef() == null)
			return new Index( row, col, MIDDLE );
		    else // all three references are used
			{ }
		}// node of current cell is available
	    }// current cell already holds a group

	}// loop on grid cells

	throw new MemoryManagerException( "Out of memory" );
    }//allocateRefAndNode method

    private Index allocateRef( Index desired ) 
	throws MemoryManagerException
    {

	//System.out.println( "in allocateRef: " + desired);
	if (!desired.equals(NULL))
	{    // a particular cell was requested
	    
	    int drow = desired.getRow();
	    int dcol = desired.getCol();
	    Group g = grid[ drow ][ dcol ];

	    if ( g == null )  // desired cell is empty

		if ( (desired.getRef() == ANY) ||
		     (desired.getRef() == LEFT) )
		    return new Index( drow, dcol, LEFT );
		else if (desired.getRef() == MIDDLE)
		    return new Index( drow, dcol, MIDDLE );
		else return new Index( drow, dcol, RIGHT );
	    
	    else // desired cell already holds a group

		if ( ((desired.getRef() == ANY) ||
		      (desired.getRef() == LEFT)) &&
		     (g.getLeftRef() == null) )
		    return new Index( drow, dcol, LEFT );
		else if ( ((desired.getRef() == ANY) ||
			   (desired.getRef() == MIDDLE)) &&
			  (g.getMiddleRef() == null) )
		    return new Index( drow, dcol, MIDDLE );
		else if ( ((desired.getRef() == ANY) ||
			   (desired.getRef() == RIGHT)) &&
			  (g.getRightRef() == null) )
		    return new Index( drow, dcol, RIGHT );
		else // all three references are used
		    { }
	
	}// the user requested a particular cell

	// the user did not specify a cell or the desired cell was not
	// available for both a node and a reference
		
	for(int col=0; col<numCols; col++)
	    for(int row=0; row<numRows; row++)
	{
	    if (grid[ row ][ col ] == null)
		return new Index(row, col, LEFT);
	    else
	     {   // current cell already holds a group g
		 Group g = grid[ row ][ col ];

		 if (g.getLeftRef() == null)
		     return new Index( row, col, LEFT );
		 else if (g.getRightRef() == null)
		     return new Index( row, col, RIGHT );
		 else if (g.getMiddleRef() == null)
		     return new Index( row, col, MIDDLE );
		 else // all three references are used
		     { }
	     }// current cell already holds a group

	}// loop on grid cells

	throw new MemoryManagerException( "Out of memory" );
    }//allocateRef method


    public Index allocateNode( String info, String pointer, 
			       int length, int drow, int dcol ) 
	throws MemoryManagerException
    {

	//System.out.println(" In allocateNode: " + info + " " + pointer +
	//" " + length + " " + drow + " " + dcol);

	if (grid[ drow ][ dcol ] == null)
	{
	    Group g = new Group();
	    g.setRow( drow );
	    g.setCol( dcol );
	    fillInNode( g, info, pointer, length );
	    grid[ drow ][ dcol ] = g;
	    return new Index(drow, dcol, NODE);
	}
	else if (grid[ drow ][ dcol ].getDataBox() == null)
	{
	    fillInNode( grid[ drow ][ dcol ], info, pointer, length );
	    return new Index(drow, dcol, NODE);
	}

	// the user did not specify a cell or the desired cell already contained a node
	// use the column-by-column layout to find an available cell
		
	for(int col=0; col<numCols; col++)
	    for(int row=0; row<numRows; row++)
	{
	    if (grid[ row ][ col ] == null)
	    {
		Group g = new Group();
		g.setRow( row );
		g.setCol( col );
		fillInNode( g, info, pointer, length );
		grid[ row ][ col ] = g;
		return new Index(row, col, NODE);
	    }
	    else
	    {   // current cell already holds a group g

		Group g = grid[ row ][ col ];

		if (g.getDataBox() == null)  // node is available
		{
		    fillInNode( g, info, pointer, length );
		    return new Index(row, col, NODE);
		}
	    }// current cell already holds a group
	    
	}// loop on grid cells
	
	throw new MemoryManagerException( "Out of memory" );
    }//allocateRefAndNode method

    private void fillInNode( Group g, String info, String pointer, int length ) 
    {

	g.setDataBox( new Box( HIGHLIGHT_COLOR ) );
	g.setDataValue( info );
	g.setNextRef( new Box( HIGHLIGHT_COLOR ) );
	if (pointer.equals( "null" ) )
	    g.setNextArrow( new Arrow( -1, -1, "null", HIGHLIGHT_COLOR) );
	else
	{
	    Index nextIndex;
	    if (length==0)
		nextIndex = getAddress( pointer );
	    else
		nextIndex = getValue( getNext( getAddress( pointer), length ));

	    g.setNextArrow( new Arrow( nextIndex.getRow(),
				       nextIndex.getCol(), 
				       "regular", 
				       HIGHLIGHT_COLOR));
	}

    }//fillInNode method

    /**
     * Allocates space for a new node in the grid in the given cell
     * if possible. Assumes that the given cell contains a reference
     * box already.
     *
     * @param   row    Row of desired location of node.
     * @param   col    Column of desired location of node.
     *
     * @return  The index of the grid cell selected according to the 
     *          default layout.
     */
    private Index allocateNode(int row, int col) 
	throws MemoryManagerException
    {
	if (grid[ row ][ col ].getDataBox() == null)	    
	    // there is room for the node
	    return new Index( row, col, NODE);
	
	// else, use the current layour manager
	if ( (currentCol == -1) || (currentCol == numCols) ||
	     (currentRow == -1) || (currentRow == numRows) )
	    throw new MemoryManagerException( "Out of memory" );

	Index index = new Index(currentRow, currentCol, NODE);

	currentRow += vDirection;
	if (currentRow == numRows) // vDirection must be +1
	{
	    currentCol += hDirection;
	    currentRow = 0;
	} else if (currentRow == -1) // vDirection must be -1
	{
	    currentCol += hDirection;
	    currentRow = numRows - 1;
	}

	return index;
	
    }//allocateNode method


    /**
     * Assigns a value to the info data member of a node.
     *
     * @param    address     Index of the node.
     * @param    info        One-letter <code>String</code> to be assigned.
     */
    public void setInfo( Index address, String info )
    {
	grid[ address.getRow() ][address.getCol()].setDataValue( info );
    }//setInfo method

    /**
     * Returns true if the pointer's value is NULL; false otherwise.
     *
     * @param    address      Address of the pointer.
     */
    public boolean isNull( Index address )
    {
	return ( getValue( address ).equals(NULL) );
    }//isNull method


    /**
     * Straightens out the linked list by redrawing its nodes one after the
     * other in a row (or a column, depending on the current layout manager).
     * The destination row (or column) is determined by the location 
     * of the head reference.
     * Assumes that no node other than those in the list are located in the 
     * destination row (or column). No references pointed to any node in the
     * list are moved.
     *
     * @param   head   The address of the head of the linked list.
     */
    public void redrawLinkedList( Index head ) throws IOException
    {
	int row = head.getRow();
	int col = head.getCol();

	Index nodes[]; // nodes making up the linked list


	    nodes = new Index[ numCols - col ];
	    int position = 0;
	    nodes[ position++ ] = getValue( head );

	    if (nodes[ position-1 ] != NULL)
		// list is not empty
	    {

		if ( getArrow( nodes[position-1]  ).getType().equals("null"))
		    // list contains exactly one node
		    moveNode( nodes[position-1].getRow(), 
			      nodes[position-1].getCol(),
			      row, col );

		else // list contains at least two nodes
		{
		    Index current = getNext( nodes[position-1], 1 );
		
		    while ( true )
		    {
			nodes[ position++ ] = current;
			if (getArrow( current ).getType().equals("null"))
			    break;
			else
			    current = getNext( nodes[position-1], 1 );
		    }
		
		    // make a temporary copy of the nodes to be moved
		    // make sure to remember their original locations
		    Group tempNodes[] = copyNodes( nodes, position );

		    // erase all the nodes to be moved
		    for(int i=0; i<position; i++)
		    {
			int r = tempNodes[i].getRow();
			int c = tempNodes[i].getCol();
			grid[ r ][ c ].setDataBox( null );
			grid[ r ][ c ].setDataValue( "" );
			grid[ r ][ c ].setNextRef( null );
			grid[ r ][ c ].setNextArrow( null );
		    }

		    // move each node to its destination
		    for(int i=0; i<position; i++)
		    {
			int r = tempNodes[i].getRow();
			int c = tempNodes[i].getCol();
			if (grid[ row ][ col+i ]==null)
			{
			    grid[ row ][ col+i ]= new Group();
			    grid[ row ][ col+i ].setRow( row );
			    grid[ row ][ col+i ].setCol( col+i );
			    
			}
			grid[ row ][ col+i ].setDataBox(
				tempNodes[i].getDataBox());
			grid[ row ][ col+i ].setDataValue(
				tempNodes[i].getDataValue());
			grid[ row ][ col+i ].setNextRef(
				tempNodes[i].getNextRef());
			if (i==position-1)
			    grid[ row ][ col+i ].setNextArrow(
			      new Arrow( -1,-1,"null", LINE_COLOR) );
			else
			    grid[ row ][ col+i ].setNextArrow(
			      new Arrow( row, col+i+1, "regular", LINE_COLOR));
		    }
	       
		    // update reference arrows that point to moved nodes
		    for(int r1=0; r1<numRows; r1++)
			for(int c1=0; c1<numCols; c1++)
		    {
			int index;
			Group g = grid[ r1 ][ c1 ];
			if (g != null)
			{
			    if (g.getLeftArrow() != null)
			    {
				index = pointsToMovedNode(g.getLeftArrow(),
							  tempNodes,position);
				if (index>=0)
				{
				    g.getLeftArrow().setRow( row );
				    g.getLeftArrow().setCol( col+index );
				}
			    }
			    if (g.getMiddleArrow() != null)
			    {
				index = pointsToMovedNode(g.getMiddleArrow(),
							  tempNodes,position);
				if (index>=0)
				{
				    g.getMiddleArrow().setRow( row );
				    g.getMiddleArrow().setCol( col+index );
				}
			    }
			    if (g.getRightArrow() != null)
			    {
				index = pointsToMovedNode(g.getRightArrow(),
							  tempNodes,position);
				if (index>=0)
				{
				    g.getRightArrow().setRow( row );
				    g.getRightArrow().setCol( col+index );
				}
			    }
			}
		    }
		}
	    }

	//show.writeSnap("", "JHAVEPOP.htm", pseudoURL + "&line=" + lineNumber, this);	
	show.writeSnap("", "JHAVEPOP.htm", make_uri(lineNumber,-1,-1,-1) ,this);
    }//redrawLinkedList method
    
    private int pointsToMovedNode( Arrow a, Group[] nodes, int n)
    {
	int row = a.getRow();
	int col = a.getCol();

	for(int i=0; i<n; i++)
	    if ( (row == nodes[i].getRow()) &&
		 (col == nodes[i].getCol()) )
		return i;
	return -1;
    }//pointsToMoveNode method

    private Group[] copyNodes( Index[] nodes, int n)
    {
	Group g[] = new Group[ n ];
	for(int i=0; i<n; i++)
	{
	    g[i] = new Group();
	    g[i].setRow( nodes[i].getRow() );
	    g[i].setCol( nodes[i].getCol() );
	    g[i].setDataBox( 
   	       grid[ nodes[i].getRow() ][ nodes[i].getCol() ].getDataBox() );
	    g[i].setDataValue( 
   	       grid[ nodes[i].getRow() ][ nodes[i].getCol() ].getDataValue() );
	    g[i].setNextRef( 
   	       grid[ nodes[i].getRow() ][ nodes[i].getCol() ].getNextRef() );
	    g[i].setNextArrow( 
   	       grid[ nodes[i].getRow() ][ nodes[i].getCol() ].getNextArrow() );
	}
	return g;
    }// copyNodes method

    public String getInfo( PointerExpression exp )
    {
 
        Index index = getValue( exp );

	if (index.equals(NULL))
 	    throw new MemoryManagerException(
	             "Trying to dereference a NULL pointer");

	return grid[ index.getRow() ][ index.getCol() ].getDataValue();
    }


    public Box getDataBox( PointerExpression exp )
    {
 
        Index index = getValue( exp );
        
	if ( index.equals( NULL ) )
	    throw new MemoryManagerException( "Trying to dereference " +
					      "a NULL pointer" );
	return grid[ index.getRow() ][ index.getCol() ].getDataBox();
    }

    public Index getValue( PointerExpression exp )
    {
	String id = exp.getPointerName();
	int length = exp.getChainLength();
	Index address;
	if (length==-1)
	    return NULL;
	else if (length==0)
	    address = getAddress(id);
	else
	    address = getNext( getAddress(id), length );

	return getValue( address );
    }//getValue method

    public boolean eval( BooleanExpression b, int lineNumber )

	throws IOException
    {
	boolean value1 = 
	    ( b.getType().equals("pointer") ?
	      compare( b.getLHSp(), b.getRHSp(), b.getComparator(),
		       lineNumber, b.getStartHighlighting(), 
		       b.getStopHighlighting() ) :
	      compare( b.getLHSd(), b.getRHSd(), b.getComparator(),
		       lineNumber, b.getStartHighlighting(), 
		       b.getStopHighlighting() ) );

	if (b instanceof CompoundBooleanExpression)
	{
            CompoundBooleanExpression cb = (CompoundBooleanExpression) b;
	    boolean AND = cb.getConnector().equals("&&");

	    if (AND && !value1) return false;
	    else if ( !AND    /* i.e., OR */ 
                      && value1 ) return true;
	    else
	    {
		BooleanExpression s = cb.getSecond();
		return 
		    ( s.getType().equals("pointer") ?
		      compare( s.getLHSp(), s.getRHSp(), s.getComparator(),
			       lineNumber+1, s.getStartHighlighting(), 
			       s.getStopHighlighting() ) :
		      compare( s.getLHSd(), s.getRHSd(), s.getComparator(),
			       lineNumber+1, s.getStartHighlighting(), 
			       s.getStopHighlighting() ) );
	    }
	}
	else
	    return value1;
        
    }

    public boolean compare( PointerExpression exp1, PointerExpression exp2,
			    String comparator, int lineNumber, 
                            int start, int end )
	throws IOException
    {
        
	//System.out.println( exp1.getPointerName() + " " + exp2.getPointerName());
	Index add1 = NULL, add2 = NULL;
	Arrow arrow1 = null, arrow2 = null;

	// visualization stuff
	this.setLineNumber( lineNumber );
	if (!exp1.isNull())
	{
            //System.out.println( getAddress( exp1 ) );
            
	    arrow1 = getArrow( getAddress( exp1 ) );
	    //System.out.println( arrow1.getRow() + " " +arrow1.getCol() );

	    arrow1.setLineColor( HIGHLIGHT_COLOR );		
	}

	if (!exp2.isNull())
	{
            //System.out.println( "here2" );
	    arrow2 = getArrow( getAddress( exp2 ) );
	    arrow2.setLineColor( HIGHLIGHT_COLOR );		
	}
        //System.out.println( "here" );

	boolean LHSisNull = exp1.isNull() || (getValue( exp1 ).equals(NULL));
	boolean RHSisNull = exp2.isNull() || (getValue( exp2 ).equals(NULL));

	//System.out.println( "LHSisNull = " + LHSisNull);
	//System.out.println( "RHSisNull = " + RHSisNull);

        //System.out.println( "value of exp1: " + getValue( exp1 ) );        
        //System.out.println( "value of exp2: " + getValue( exp2 ) );
	boolean value = 		
	    ( (LHSisNull && RHSisNull) || 
	      ((!LHSisNull && !RHSisNull) &&
	       (getValue( exp1 ).equals( getValue( exp2 ) ) ) ) );

	//System.out.println( "value = " + value);

        if (start>-1) //show.writeSnap("", "JHAVEPOP.htm", pseudoURL + "&line=" + lineNumber + "&amp;start=" + start + "&amp;end=" + end, this);
	    show.writeSnap("", "JHAVEPOP.htm", make_uri(lineNumber,start,end,-1) ,this);
        else //show.writeSnap("", "JHAVEPOP.htm", pseudoURL + "&line=" + lineNumber, this);
	    show.writeSnap("", "JHAVEPOP.htm", make_uri(lineNumber,-1,-1,-1) ,this);

	if (!exp1.isNull())
	    arrow1.setLineColor( LINE_COLOR );		
	if (!exp2.isNull())
	    arrow2.setLineColor( LINE_COLOR );		

	if (comparator.equals( "==" ))
	    return value;
	else
	    return !value;
    }//compare method

    /**
     * Comparison of DataExpressions
     */
    public boolean compare( DataExpression exp1, DataExpression exp2,
			    String comparator, int lineNumber, 
                            int start, int end )
	throws IOException
    {
        String char1 = exp1.getCharacter();
	String char2 = exp2.getCharacter();
	Box box1=null, 
	    box2=null;

	if (char1.equals(""))
        {
	    PointerExpression pexpr = exp1.getPointerExpression();
	    char1 = getInfo( pexpr );

	    box1 = getDataBox( pexpr );
	    box1.setLineColor( HIGHLIGHT_COLOR );		
	}

	if (char2.equals(""))
        {
	    PointerExpression pexpr = exp2.getPointerExpression();
	    char2 = getInfo( pexpr );

	    box2 = getDataBox( pexpr );
	    box2.setLineColor( HIGHLIGHT_COLOR );		
	}


	boolean value = char1.equals( char2 );

        if (start>-1) //show.writeSnap("", "JHAVEPOP.htm", pseudoURL + "&line=" + lineNumber + "&amp;start=" + start + "&amp;end=" + end, this);
	    show.writeSnap("", "JHAVEPOP.htm", make_uri(lineNumber,start,end,-1) ,this);
        else //show.writeSnap("", "JHAVEPOP.htm", pseudoURL + "&line=" + lineNumber, this);
	    show.writeSnap("", "JHAVEPOP.htm", make_uri(lineNumber,-1,-1,-1) ,this);

	if (box1 != null)
	    box1.setLineColor( LINE_COLOR );		
	if (box2 != null)
	    box2.setLineColor( LINE_COLOR );		

	if (comparator.equals( "==" ))
	    return value;
	else
	    return !value;
    }//compare method

    public void dataAssign( PointerExpression LHS, String RHS, 
			    int lineNumber )
	throws IOException
    {

	System.out.println( "here: "  + RHS);

	Box box1 = getDataBox( LHS );
	box1.setLineColor( HIGHLIGHT_COLOR );		

	Index index = getValue( getAddress( LHS ) );
	grid[ index.getRow() ][ index.getCol() ].setDataValue( RHS );

	//show.writeSnap("", "JHAVEPOP.htm", pseudoURL + "&line=" + lineNumber,  this);
	show.writeSnap("", "JHAVEPOP.htm", make_uri(lineNumber,-1,-1,-1) ,this);

	box1.setLineColor( LINE_COLOR );


    }//dataAssign with character as RHS


    public void dataAssign( PointerExpression LHS, PointerExpression RHS,
			    int lineNumber ) 
	throws IOException
    {

	Box box1 = getDataBox( LHS );
	box1.setLineColor( HIGHLIGHT_COLOR );		

	String RHSchar = getInfo( RHS );

	Index index = getValue( getAddress( LHS ) );
	grid[ index.getRow() ][ index.getCol() ].setDataValue( RHSchar );

	//show.writeSnap("", "JHAVEPOP.htm", pseudoURL + "&line=" + lineNumber,  this);
	show.writeSnap("", "JHAVEPOP.htm", make_uri(lineNumber,-1,-1,-1) ,this); 

	box1.setLineColor( LINE_COLOR );		


    }//dataAssign with pointer expression as RHS

    /**
     * Returns true if ...
     *
     * @param    name         Name of the pointer variable.
     */
    public boolean isNull( String name )
    {
	return ( getValue( name ).equals(NULL) );
    }//isNull method

    /**
     * Moves a node (info + next pointer) from one grid cell to another.
     * The references pointing to the node are also updated accordingly.
     * Note that the destination cell may not contain a node but it may
     * contain one or more reference cells that must be preserved.
     *
     * @param    fromRow   Current row occupied by the node.
     * @param    fromCol   Current column occupied by the node.
     * @param    toRow     Row to which the node is moving.
     * @param    toCol     Column to which the node is moving.
     */
    private void moveNode( int fromRow, int fromCol, int toRow, int toCol)

    {
	Group from = grid[ fromRow ][ fromCol ];

	Group g = new Group();
	g.setRow( toRow );
	g.setCol( toCol );

	// preserve reference cells
	if (grid[ toRow ][ toCol ] != null)
	{
	    g.setLeftRef( grid[ toRow ][ toCol ].getLeftRef() );
	    g.setLeftArrow( grid[ toRow ][ toCol ].getLeftArrow() );
	    g.setLeftLabel( grid[ toRow ][ toCol ].getLeftLabel() );
	    g.setLeftLabelPosition( 
   	          grid[ toRow ][ toCol ].getLeftLabelPosition() );	    
	    g.setMiddleRef( grid[ toRow ][ toCol ].getMiddleRef() );
	    g.setMiddleArrow( grid[ toRow ][ toCol ].getMiddleArrow() );
	    g.setMiddleLabel( grid[ toRow ][ toCol ].getMiddleLabel() );
	    g.setMiddleLabelPosition( 
   	          grid[ toRow ][ toCol ].getMiddleLabelPosition() );
	    g.setRightRef( grid[ toRow ][ toCol ].getRightRef() );
	    g.setRightArrow( grid[ toRow ][ toCol ].getRightArrow() );
	    g.setRightLabel( grid[ toRow ][ toCol ].getRightLabel() );
	    g.setRightLabelPosition( 
   	          grid[ toRow ][ toCol ].getRightLabelPosition() );
	}

	// copy new node
	g.setDataBox( from.getDataBox() );
	g.setDataValue( from.getDataValue() );
	g.setNextRef( from.getNextRef() );
	g.setNextArrow( from.getNextArrow() );

	// update arrows going into the moved node
	for(int row=0; row<numRows; row++)
	    for(int col=0; col<numCols; col++)
	    {
		if (grid[ row ][ col ] != null)
		{
		    Group g1 = grid[ row ][ col ];

		    if ((g1.getLeftArrow() != null) &&
			(g1.getLeftArrow().getRow() == fromRow) &&
			(g1.getLeftArrow().getCol() == fromCol) )
		    {
			g1.getLeftArrow().setRow( toRow );
			g1.getLeftArrow().setCol( toCol );
		    }

		    if ((g1.getMiddleArrow() != null) &&
			(g1.getMiddleArrow().getRow() == fromRow) &&
			(g1.getMiddleArrow().getCol() == fromCol) )
		    {
			g1.getMiddleArrow().setRow( toRow );
			g1.getMiddleArrow().setCol( toCol );
		    }
		    
		    if ((g1.getRightArrow() != null) &&
			(g1.getRightArrow().getRow() == fromRow) &&
			(g1.getRightArrow().getCol() == fromCol) )
			{
			    g1.getRightArrow().setRow( toRow );
			    g1.getRightArrow().setCol( toCol );
		    }

		    if ((g1.getNextArrow() != null) &&
			(g1.getNextArrow().getRow() == fromRow) &&
			(g1.getNextArrow().getCol() == fromCol) )
		    {
			g1.getNextArrow().setRow( toRow ) ;
			g1.getNextArrow().setCol( toCol );
		    }
		}
	    }

	grid[ toRow ][ toCol ] = g;

	grid[ fromRow ][ fromCol ].setDataBox( null );
	grid[ fromRow ][ fromCol ].setDataValue( "" );
	grid[ fromRow ][ fromCol ].setNextRef( null );	
	grid[ fromRow ][ fromCol ].setNextArrow( null );
    }//moveNode method

    /**
     * Returns the arrow leading out of a memory location,that is, the
     * value of the corresponding pointer.
     *
     * @param  index  The address of the pointer
     */
    private Arrow getArrow( Index index ) 
    {
        if (index.equals(NULL))
	    throw new MemoryManagerException( "Trying to dereference an " +
                         "uninitialized pointer");

	int row = index.getRow();
	int col = index.getCol();
	int id = index.getRef();
        Arrow arrow = null;

	switch (id) { 
	case LEFT: 
	    arrow = grid[ row ][ col ].getLeftArrow();
            break;
	case MIDDLE: 
	    arrow = grid[ row ][ col ].getMiddleArrow();
            break;
	case RIGHT: 
	    arrow = grid[ row ][ col ].getRightArrow();
            break;
	default: 
	    arrow = grid[ row ][ col ].getNextArrow();
            break;
	}// switch on reference box

        if (arrow == null)
	    throw new MemoryManagerException( "Trying to dereference an " +
                         "uninitialized pointer");
	else
	    return arrow;

    }// getArrow method

    public void doBold(Index address)
    {
	Group g = grid[ address.getRow() ][ address.getCol() ];
	switch (address.getRef()) {
	case LEFT:  g.getLeftRef().setLineThickness( "bold");  break;
	case MIDDLE:  g.getMiddleRef().setLineThickness( "bold");  break;
	case RIGHT:  g.getRightRef().setLineThickness( "bold");  break;
	case NODE:  g.getNextRef().setLineThickness( "bold");  break;
	}//switch
    }//doBold method

    public void undoBold(Index address)
    {
	Group g = grid[ address.getRow() ][ address.getCol() ];
	switch (address.getRef()) {
	case LEFT:  g.getLeftRef().setLineThickness( "normal" );  break;
	case MIDDLE:  g.getMiddleRef().setLineThickness( "normal" );  break;
	case RIGHT:  g.getRightRef().setLineThickness( "normal" );  break;
	case NODE:  g.getNextRef().setLineThickness( "normal" );  break;
	}//switch
    }//undoBold method

    private Index makeIndex( int row, int col, String ref )
    {
	if (( row<0) || (row>=numRows) || (col<0) || (col>=numCols))
	    return NULL;

	if (ref.equals("LEFT"))
	    return new Index(row, col, LEFT);
	else if (ref.equals("MIDDLE"))
	    return new Index(row, col, MIDDLE);
	else if (ref.equals("RIGHT"))
	    return new Index(row, col, RIGHT);
	else // ref == ""
	    if ((row==-1) && (col==-1))
		return NULL;
	    else
		return new Index(row, col, ANY);
    }//makeIndex method

    public void createLinkedList(String head, String[] elements, String tail)
	throws IOException
    {
	int n = elements.length;
	if (n>numCols)
	    throw new MemoryManagerException( "Too few columns to fit linked list "
					      + head);

	// set up the head of the list
	Index index = allocateRefAndNode(  makeIndex( 0,0,"LEFT") );

	int row = index.getRow();
	int col = index.getCol();
	Group g = new Group();
	g.setRow( row );
	g.setCol( col );

	g.setLeftRef( new Box( HIGHLIGHT_COLOR ) );
	g.setLeftLabel( head );
	g.setLeftArrow( new Arrow( row, col, "regular", HIGHLIGHT_COLOR ));
	g.setDataBox( new Box( HIGHLIGHT_COLOR ) );
	g.setDataValue( elements[0] );
	g.setNextRef( new Box( HIGHLIGHT_COLOR ) );
	g.setNextArrow( new Arrow( 0, 1, "regular", HIGHLIGHT_COLOR) );
	grid[ row ][ col ] = g;
	map.put( head, index );

	for(int i=1; i<n; i++)
	{
	    if (i>1)
		grid[0][i-1].setNextArrow( new Arrow( 0, i, "regular", HIGHLIGHT_COLOR) );
	    allocateNode( elements[i], "null", -1, 0, i );	
	    
	}

	if (!tail.equals(""))
	{
	    grid[0][n-1].setLeftRef( new Box( HIGHLIGHT_COLOR ) );
	    grid[0][n-1].setLeftLabel( tail );
	    grid[0][n-1].setLeftArrow( new Arrow( 0, n-1, "regular", HIGHLIGHT_COLOR ));
	    map.put( tail, makeIndex(0, n-1, "LEFT") );
	}

	//show.writeSnap("", "JHAVEPOP.htm", pseudoURL + "&line=1", this);
	show.writeSnap("", "JHAVEPOP.htm", make_uri(1,0,0,0) ,this);

	g.getLeftRef().setLineColor( LINE_COLOR );
	g.getLeftArrow().setLineColor( LINE_COLOR );
	for(int i=0; i<n; i++)
	{
	    grid[0][i].getDataBox().setLineColor( LINE_COLOR );
	    grid[0][i].getNextRef().setLineColor( LINE_COLOR );
	    grid[0][i].getNextArrow().setLineColor( LINE_COLOR );
	}
	if (!tail.equals(""))
	{
	    grid[0][n-1].getLeftRef().setLineColor( LINE_COLOR );
	    grid[0][n-1].getLeftArrow().setLineColor( LINE_COLOR );
	}
    }//createLinkedList method

    //---------------------- XML Methods -----------------------------

    /**
    * Creates and returns GAIGS XML code for the current state of the grid
    *
    * @return     A String containing GAIGS XML code for the linked list
    */

    public String toXML()
    {
	String XML;
	
	XML = 
	    "  <memorylayout rows=\"" + numRows + "\" cols=\"" + numCols + 
	    "\" horiz_spacing=\"" + horizSpacing + 
	    "\" debug_flag=\"" +
	    ( debugMode ? "on" : "off" ) +
	    "\">\n" +
	    "    <bounds x1=\"" + x1 + "\" y1=\"" + y1 + "\" x2=\"" + x2 +
                     "\" y2=\"" + y2 + "\" fontsize=\"0.1\"/>\n";

	
	for(int row=0; row<numRows; row++)
	    for(int col=0; col<numCols; col++)
		if (grid[ row ][ col ] != null)
		    XML += grid[ row ][ col ].toXML();

	XML += "  </memorylayout>\n";
	
        return XML;
    }//toXML method

    /********************************************************************
     *
     *  inner class: Arrow
     *
     *******************************************************************/

    class Arrow
    {
	int row, col;       // destination group
	String type;        // "regular" or "null"
	String lineColor; 

	Arrow(int r, int c, String t, String lc)
	{
	    row = r;
	    col = c;
	    type = t;
	    lineColor = lc;
	}// constructor

	int getRow()          { return row; }
	int getCol()          { return col; }
	String getType()      { return type; }
	String getLineColor() { return lineColor; }


	void setRow(int row)  { this.row = row; }
	void setCol(int col)  { this.col = col; }
	void setType(String t){ type = t; }
	void setLineColor(String lc) { lineColor = lc; }

	public String toXML()
	{
	    return

		"        <arrow" 
		+ ( getType().equals( "null" ) 
		    ?
		    " type=\"null\"" 
		    :
		    " row=\"" + getRow() + "\" col=\"" + getCol() + "\"" 
		    )
		+ " line_color=\"" + getLineColor() + "\"" 
		+ "/>\n";	   
	}//toXML method


    }//Arrow class



    /********************************************************************
     *
     *  inner class: Box
     *
     *******************************************************************/


    class Box
    {
	String lineColor;
	String lineThickness;
	String fillColor;
	
	Box()
	{
	    lineColor = LINE_COLOR;
	    lineThickness = "normal";
	    fillColor = FILL_COLOR;
	}// default constructor

	Box (String lc)
	{
	    lineColor = lc;
	    lineThickness = "normal";
	    fillColor = FILL_COLOR;
	}// constructor

	Box (String lc, String lt)
	{
	    lineColor = lc;
	    lineThickness = lt;
	    fillColor = FILL_COLOR;
	}// constructor

	Box (String lc, String lt, String fc)
	{
	    lineColor = lc;
	    lineThickness = lt;
	    fillColor = fc;
	}// constructor


	String getLineColor()      { return lineColor; }
	String getLineThickness()  { return lineThickness; }
	String getFillColor()      { return fillColor;}
	void setLineColor(String lc)      { lineColor = lc; }
	void setLineThickness(String lt)  { lineThickness = lt; }
	void setFillColor(String fc)      { fillColor = fc;}

	public String toXML()
	{
	    return
	    
		"        <box" 
		+ 
		 " line_color=\"" + getLineColor() + "\""
		+
		(getLineThickness().equals( "bold" ) ?
		 " line_thickness=\"bold\"" : "")
		+
		" fill_color=\"" + getFillColor() + "\"" 
		+
		"/>\n";

	}//toXML method


    }//Box class



    /********************************************************************
     *
     *  inner class: Group
     *
     *******************************************************************/

    class Group
    {
	int row, col;

	Box leftRef, middleRef, rightRef;

	Box dataBox;
	String dataValue;

	Box nextRef;
	String leftLabel, middleLabel, rightLabel;
	String leftLabelPosition, middleLabelPosition, rightLabelPosition;
	Arrow leftArrow, middleArrow, rightArrow;
	Arrow nextArrow;

	boolean orphanNode;

	public Group()
	{
	    leftLabelPosition = "";
            orphanNode = false;
	}

	void setRow( int r )               { row = r; }
	void setCol( int c )               { col = c; }
	void setLeftRef( Box b )           { leftRef = b; }
	void setRightRef( Box b )          { rightRef = b; }
	void setMiddleRef( Box b )         { middleRef = b; }
	void setDataBox( Box b )           { dataBox = b; }
	void setDataValue( String v )      { dataValue = v; }
	void setNextRef( Box b )           { nextRef = b; }
	void setLeftLabel( String lbl )    { leftLabel = lbl; }
	void setMiddleLabel( String lbl )  { middleLabel = lbl; }
	void setRightLabel( String lbl )   { rightLabel = lbl; }
	void setLeftArrow( Arrow arrow )   { leftArrow = arrow; }
	void setRightArrow( Arrow arrow )  { rightArrow = arrow; }
	void setMiddleArrow( Arrow arrow ) { middleArrow = arrow; }	
	void setNextArrow( Arrow arrow )   { nextArrow = arrow; }
	void setLeftLabelPosition( String pos )    { leftLabelPosition = pos; }
	void setMiddleLabelPosition( String pos ) { middleLabelPosition = pos;}
	void setRightLabelPosition( String pos )  { rightLabelPosition = pos; }

	int getRow()               { return row; }
	int getCol()               { return col; }
	Box getLeftRef()           { return leftRef; }
	Box getRightRef()          { return rightRef; }
	Box getMiddleRef()         { return middleRef; }
	Box getDataBox()           { return dataBox; }
	String getDataValue()      { return dataValue; }
	Box getNextRef()           { return nextRef; }
	String getLeftLabel()      { return leftLabel; }
	String getMiddleLabel()    { return middleLabel; }
	String getRightLabel()     { return rightLabel; }
	Arrow getLeftArrow()       { return leftArrow; }
	Arrow getRightArrow()      { return rightArrow; }
	Arrow getMiddleArrow()     { return middleArrow; }
	Arrow getNextArrow()       { return nextArrow; }
	String getLeftLabelPosition()      { return leftLabelPosition; }
	String getMiddleLabelPosition()    { return middleLabelPosition; }
	String getRightLabelPosition()     { return rightLabelPosition; }

	public String toXML()
	{
	    String XML = 
		"    <group row=\"" + getRow() + "\" col=\"" + 
		getCol() + "\">\n";

	    if (leftRef != null)
		XML += 
		    "      <left_ref>\n" +
		    "        <ref_label" +
		    ( getLeftLabelPosition().equals("left")
		      ? " position=\"left\"" : "" ) +
		    ">" +
		    getLeftLabel() + "</ref_label>\n" +
		    leftRef.toXML() +
		    ( getLeftArrow()!=null ?
		      getLeftArrow().toXML() : "" ) +
		    "      </left_ref>\n";

	    if (middleRef != null)
		XML += 
		    "      <middle_ref>\n" +
		    "        <ref_label" +
		    ( getMiddleLabelPosition().equals("left")
		      ? " position=\"left\"" : "" ) +
		    ">" +
		    getMiddleLabel() + "</ref_label>\n" +
		    middleRef.toXML() +
		    ( getMiddleArrow()!=null ?
		      getMiddleArrow().toXML() : "" ) +
		    "      </middle_ref>\n";

	    if (rightRef != null)
		XML += 
		    "      <right_ref>\n" +
		    "        <ref_label" +
		    ( getRightLabelPosition().equals("left")
		      ? " position=\"left\"" : "" ) +
		    ">" +
		    getRightLabel() + "</ref_label>\n" +
		    rightRef.toXML() +
		    ( getRightArrow()!=null ?
		      getRightArrow().toXML() : "" ) +
		    "      </right_ref>\n";

	    if (dataBox != null)
		XML += 
		    "      <data value=\"" + dataValue + "\">\n" +
		    dataBox.toXML() +
		    "      </data>\n";

	    if (nextRef != null)
		XML += 
		    "      <next>\n" +
		    nextRef.toXML() +
		    ( getNextArrow()!=null ?
		      getNextArrow().toXML() : "" ) +
		    "      </next>\n";

	    XML += "    </group>\n";

	    return XML;

	}//toXML method

    }//Group class

    /********************************************************************
     *
     *  inner class: Index
     *
     *******************************************************************/

    class Index
    {
	private int row, col, ref;

	Index(int row, int col, int ref)
	{	    this.row = row;
	    this.col = col;
	    this.ref = ref;
	}// constructor

	int getRow() { return row; }
	int getCol() { return col; }
	int getRef() { return ref; }

	public String toString()
	{
	    return "[" + row + "," + col + "," + ref + "]";
	}

	public boolean equals( Index index )
	{
	    return 
		( (this.row == index.row) &&
		  (this.col == index.col) &&
		  (this.ref == index.ref) );
	}//equals method
    }//Index class


}//GAIGSMemoryManager


